/**
 * Copyright 2010, Lars J. Nilsson <http://www.larsan.net> 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.larsan.dconf.cli;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;

import net.larsan.dconf.DNode;
import net.larsan.dconf.DNodeStore;
import net.larsan.dconf.DPath;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.log4j.Logger;

import com.google.inject.Inject;

public abstract class AbstractCommand implements Command {

	@Inject
	protected DNodeStore store;
	
	@Inject
	protected Connector conn;

	private final String[] names;

	private final String description;
	
	protected final Logger log = Logger.getLogger(getClass());

	protected AbstractCommand(String[] names, String description) {
		this.description = description;
		this.names = names;
	}
	
	public AbstractCommand(String name, String description) {
		this(new String[] { name }, description);
	}
	
	@Override
	public String getDescription() {
		return description;
	}

	@Override
	public String[] getNames() {
		return names;
	}
	
	@Override
	public void exec(CommandLine cl) {
		if(cl.hasOption("h")) {
			HelpFormatter f = new HelpFormatter();
			f.printHelp(nameString(getNames()) + " " + getArgumentSyntax(), getOptions());
		} else {
			doExec(cl);
		}
	}

	@Override
	public Options getOptions() {
		Options o = new Options();
		o.addOption("h", "help", false, "Print help message");
		prepare(o);
		return o;
	}
	
	public String getArgumentSyntax() {
		return "";
	}
	
	
	// --- PROTECTED METHODS --- //
	
	protected OutputStream getDefaultOutput() {
		return conn.getOutputHandler().getDefaultOutput();
	}
	
	protected File createFileForOutput(String name) {
		return conn.getOutputHandler().newFile(name);
	}
	
	protected File createFileForInput(String name) {
		return conn.getInputHandler().newFile(name);
	}
	
	protected URL createURLForInput(String name) throws MalformedURLException {
		return conn.getInputHandler().newURL(name);
	}
	
	protected void safeClose(InputStream in) {
		if(in == null) return;
		try {
			in.close();
		} catch(IOException e) { }
	}
	
	protected void safeClose(OutputStream out) {
		if(out == null) return;
		try {
			out.close();
		} catch(IOException e) { }
	}
	
	protected DNode findNodeFromOption(CommandLine cl, String opt) {
		DNode node = store.get(conn.getCurrentPath(), false);
		if(cl.hasOption(opt)) {
			DPath next = nullNavigate(cl.getOptionValue(opt));
			if(next == null) {
				err("Illegal path: " + cl.getOptionValue(opt));
				return null;
			}
			node = store.get(next, false);
			if(node == null) {
				err("No such node: " + next);
				return null;
			}
		}
		return node;
	}
	
	protected DNode findNodeFromArg(CommandLine cl, int argIndex) {
		DNode node = store.get(conn.getCurrentPath());
		if(cl.getArgs().length > argIndex) {
			DPath next = nullNavigate(cl.getArgs()[argIndex]);
			if(next == null) {
				err("Illegal path: " + cl.getArgs()[argIndex]);
				return null;
			}
			node = store.get(next);
			if(node == null) {
				err("No such node: " + next);
				return null;
			}
		}
		return node;
	}
	
	protected DPath nullNavigate(String name) {
		try {
			return conn.getCurrentPath().navigate(name);
		} catch (ParseException e) { 
			return null;
		}
	}
	
	protected String nameString(String[] names) {
		StringBuilder b = new StringBuilder();
		for (int i = 0; i < names.length; i++) {
			b.append(names[i]);
			if(i + 1  < names.length) {
				b.append("|");
			}
		}
		return b.toString();
	}
	
	protected boolean checkDomainName(String name) {
		if(name.indexOf(':') != -1) {
			err("Domain name must not contain ':'");
			return false;
		} else if(name.indexOf('/') != -1) {
			err("Domain name must not contain '/'");
			return false;
		} else {
			return true;
		}
	}
	
	protected void out(String msg) {
		conn.getOutputHandler().printOut(msg, true);
	}
	
	protected void err(String msg) {
		conn.getOutputHandler().printErr(msg, true);
	}
	
	protected abstract void doExec(CommandLine cl);

	protected void prepare(Options o) { }
}
